/**
 * @module props
 * @version 0.0.0
 */

/**
 * @example
 * 过滤props中的属性propA、propB和propC
 filter(props, ['propA', 'propB', 'propC']) //返回去除propA、propB、propC后的props
 *
 * @example
 * 过滤props中的由Component.propTypes的key指定的的属性名
 filter(props, Component.propTypes); //返回去除Component.propTypes的key指定的属性名后的props
 */

/**
 * 过滤
 * @param {Object} props - 要过滤的props
 * @param {...String | String[] | Object} [rawFilters] - “要过滤的key”组成的数组或以“要过滤的key”作为key的对象
 * @returns {Object} - 过滤后的props
 */
export function filter(props, ...rawFilters) {
	const filters = []
	rawFilters.forEach(rawFilter => {
		switch (typeof rawFilter) {
			case 'object':
				filters.push(
					...(Array.isArray(rawFilter) ? rawFilter : Object.keys(rawFilter))
				)
			default:
				filters.push(rawFilter)
		}
	})
	const filtered = {}
	Object.keys(props).forEach(key => {
		if (!~filters.indexOf(key)) {
			filtered[key] = props[key]
		}
	})
	return filtered
}

/**
 * 当xxx变化时的回调
 * @callback module:props~onChangeXxx
 * @param {Object} newState - 新的state
 * @param {*} newVal - 新的值
 * @param {*} val - 当前的值
 * @returns {Object} 新的newState
 */

/**
 * props或state变化的信息
 * @typedef {Object} module:props~change
 * @property {String} name - 变化的props或state名称
 * @property {*} newVal - 新的值
 * @property {*} val - 当前的值
 */

/**
 * 当有任何props或state变化时的回调
 * @callback module:props~onChange
 * @param {Object} newState - 新的state
 * @param {module:props~change[]} changes - 变化的信息组成的数组
 * @returns {Object} 新的newState
 */

/**
 * 当props或state变化时才返回非null的getDerivedStateFromProps
 * @param {Object} opts - 参数
 * 		@param {Object} opts.props - props
 * 		@param {Object} opts.state - state
 * 		@param {String[]} opts.names - 仅当该数组中的属性名变化时才返回非null
 * 		@param {String} [opts.prevPattern = '_prev${Name}'] - “用于储存‘props上次变化时的值’的state属性名”的模式（模式中的'${name}'会替换成属性名；'${Name}'会替换成首字母大写的属性名）
 * 		@param {module:props~onChangeXxx} [opts.onChangeXxx] - 当props变化时的回调（其中Xxx表示要监听的属性名）
 * 		@param {module:props~onChange} [opts.onChange] - 当有任何props变化时的回调
 * @returns {Object | null} 若state有变化则返回新的state，否则返回null
 */
export function getDerivedStateWhenPropsChange(opts) {
	const { props, state, names, prevPattern = '_prev${Name}', onChange } = opts
	let ret = null
	const changes = []
	names.forEach(name => {
		const capitalizedName = name.replace(/./, str => str.toUpperCase()) //首字母大写的name
		const prevPropsName = prevPattern
			.replace(/\${name}/g, name)
			.replace(/\${Name}/g, capitalizedName)
		const propsVal = props[name]
		const prevPropsVal = state[prevPropsName]
		if (propsVal !== prevPropsVal) {
			ret = ret || {}
			ret[prevPropsName] = propsVal
			const onChangeXxx = opts[`onChange${capitalizedName}`]
			ret = onChangeXxx ? onChangeXxx(ret, propsVal, prevPropsVal) : ret
			changes.push({
				name,
				newVal: propsVal,
				val: prevPropsVal
			})
		}
	})
	if (changes.length) {
		ret = ret || {}
		ret = onChange ? onChange(ret, changes) : ret
	}
	return ret
}

/**
 * 当props或state变化时才返回非null的getDerivedStateFromProps
 * @param {Object} opts - 参数
 * 		@param {Object} opts.props - props
 * 		@param {Object} opts.state - state
 * 		@param {String[]} opts.names - 仅当该数组中的属性名变化时才返回非null
 * 		@param {String} [opts.prevPropsPattern = '_prevProps${Name}'] - “用于储存‘props上次变化时的值’的state属性名”的模式（模式中的'${name}'会替换成属性名；'${Name}'会替换成首字母大写的属性名）
 * 		@param {String} [opts.prevStatePattern = '_prevState${Name}'] - “用于储存‘state上次变化时的值’的state属性名”的模式（模式中的'${name}'会替换成属性名；'${Name}'会替换成首字母大写的属性名）
 * 		@param {module:props~onChangeXxx} [opts.onPropsChangeXxx] - 当props变化时的回调（其中Xxx表示要监听的属性名）
 * 		@param {module:props~onChangeXxx} [opts.onStateChangeXxx] - 当state变化时的回调（其中Xxx表示要监听的属性名）
 * 		@param {module:props~onChange} [opts.onPropsChange] - 当有任何props变化时的回调
 * 		@param {module:props~onChange} [opts.onStateChange] - 当有任何state变化时的回调
 * @returns {Object | null} 若state有变化则返回新的state，否则返回null
 */
export function getDerivedStateWhenPropsOrStateChange(opts) {
	const {
		props,
		state,
		names,
		prevPropsPattern = '_prevProps${Name}',
		prevStatePattern = '_prevState${Name}',
		onPropsChange,
		onStateChange
	} = opts
	let ret = null
	const propsChanges = []
	const stateChanges = []
	names.forEach(name => {
		const capitalizedName = name.replace(/./, str => str.toUpperCase()) //首字母大写的name
		const prevPropsName = prevPropsPattern
			.replace(/\${name}/g, name)
			.replace(/\${Name}/g, capitalizedName)
		const prevStateName = prevStatePattern
			.replace(/\${name}/g, name)
			.replace(/\${Name}/g, capitalizedName)
		const propsVal = props[name]
		const prevPropsVal = state[prevPropsName]
		if (propsVal !== prevPropsVal) {
			ret = ret || {}
			ret[prevPropsName] = propsVal
			const onPropsChangeXxx = opts[`onPropsChange${capitalizedName}`]
			ret = onPropsChangeXxx
				? onPropsChangeXxx(ret, propsVal, prevPropsVal)
				: ret
			propsChanges.push({
				name,
				newVal: propsVal,
				val: prevPropsVal
			})
		}
		const stateVal = state[name]
		const newStateVal = propsVal !== prevPropsVal ? propsVal : stateVal
		const prevStateVal = state[prevStateName]
		if (newStateVal !== prevStateVal) {
			ret = ret || {}
			ret[name] = newStateVal
			ret[prevStateName] = newStateVal
			const onStateChangeXxx = opts[`onStateChange${capitalizedName}`]
			ret = onStateChangeXxx
				? onStateChangeXxx(ret, newStateVal, prevStateVal)
				: ret
			stateChanges.push({
				name,
				newVal: newStateVal,
				val: prevStateVal
			})
		}
	})
	if (propsChanges.length) {
		ret = ret || {}
		ret = onPropsChange ? onPropsChange(ret, propsChanges) : ret
	}
	if (stateChanges.length) {
		ret = ret || {}
		ret = onStateChange ? onStateChange(ret, stateChanges) : ret
	}
	return ret
}
